﻿using System.Collections.Generic;
using UnityEngine;

namespace HotPlayer.Physic2D
{
    public class SAT
    {
        public static bool IsColliding(Vector2[] polygon1, Vector2[] polygon2)
        {
            List<Vector2> axes = new List<Vector2>();
            GetAxes(polygon1, axes);
            GetAxes(polygon2, axes);

            foreach (Vector2 axis in axes)
            {
                if (!IsOverlap(axis, polygon1, polygon2))
                {
                    return false;
                }
            }

            return true;
        }
        
        public static bool IsColliding(Polygon2D polygon1, Polygon2D polygon2, out Vector2 normal, out float mtv)
        {
            List<Vector2> axes = new List<Vector2>();
            GetAxes(polygon1.pointsWS, axes);
            GetAxes(polygon2.pointsWS, axes);

            normal = new Vector2();

            mtv = float.MaxValue;

            foreach (Vector2 axis in axes)
            {
                if (!IsOverlap(axis, polygon1.pointsWS, polygon2.pointsWS, out var distance))
                {
                    return false;
                }

                if (Mathf.Abs(distance) < mtv)
                {
                    mtv = distance;
                    normal = axis;
                }
            }

            return true;
        }

        public static Vector2 GetContactPoint(Polygon2D polygon1, Polygon2D polygon2, Vector2 direction, float mtv)
        {
            var point1 = polygon1.GetFarthestPointInDirection(-direction);
            var center1 = polygon1.AveragePoint();
            var point2 = polygon2.GetFarthestPointInDirection(direction);
            var center2 = polygon2.AveragePoint();
            var center = center1 + center2;
            if (Vector2.Distance(point1, center) < Vector2.Distance(point2, center))
            {
                return point1 + mtv * direction;
            }
            return point2 + mtv * -direction;
        }

        private static void GetAxes(Vector2[] polygon, List<Vector2> axes)
        {
            for (int i = 0; i < polygon.Length; i++)
            {
                Vector2 p1 = polygon[i];
                Vector2 p2 = polygon[i + 1 == polygon.Length ? 0 : i + 1];
                Vector2 edge = p2 - p1;
                Vector2 normal = new Vector2(-edge.y, edge.x).normalized;
                axes.Add(normal);
            }
        }
        
        private static bool IsOverlap(Vector2 axis, Vector2[] polygon1, Vector2[] polygon2, out float distance)
        {
            var range1 = Project(axis, polygon1);
            var min1 = range1.x;
            var max1 = range1.y;
            var range2 = Project(axis, polygon2);
            var min2 = range2.x;
            var max2 = range2.y;
            var d1 = max1 - min2;
            var d2 = max2 - min1;
            distance = Mathf.Min(d1, d2);
            return max1 >= min2 && max2 >= min1;
        }

        private static bool IsOverlap(Vector2 axis, Vector2[] polygon1, Vector2[] polygon2)
        {
            var range1 = Project(axis, polygon1);
            var min1 = range1.x;
            var max1 = range1.y;
            var range2 = Project(axis, polygon2);
            float min2 = range2.x;
            float max2 = range2.y;
            return max1 >= min2 && max2 >= min1;
        }

        private static Vector2 Project(Vector2 axis, Vector2[] polygon)
        {
            float min = Vector2.Dot(axis, polygon[0]);
            float max = min;

            for (int i = 1; i < polygon.Length; i++)
            {
                float projection = Vector2.Dot(axis, polygon[i]);
                if (projection < min) min = projection;
                if (projection > max) max = projection;
            }

            return new Vector2(min, max);
        }
    }
}